home *** CD-ROM | disk | FTP | other *** search
/ Experimental BBS Explossion 3 / Experimental BBS Explossion III.iso / compress / acmp_300.zip / DCCMP.C < prev    next >
Text File  |  1991-02-17  |  37KB  |  1,133 lines

  1.  
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <malloc.h>
  6. #include <process.h>
  7. #include <errno.h>
  8. #include <dos.h>
  9. #include <sys\types.h>
  10. #include <sys\stat.h>
  11.  
  12.  
  13. #define VERSION    "1.02"        /* Version of this code                     */
  14.  
  15. #define START        0L
  16.  
  17. #define HOURS        1
  18. #define MINS         2
  19.  
  20. #define ERROR       -1L          /* If a program in batch had an error       */
  21. #define COMMENT     -2L          /* If this line is a comment header         */
  22.  
  23. #define DSIZE      300
  24. #define INSZ       256
  25. #define PATHSZ     100
  26. #define PATSZ       50
  27. #define LABSZ       50
  28.  
  29.  
  30. char *norm(char *file);
  31. int   compare();
  32. void  getpath(char *cur);
  33. void  setpath(char *s);
  34. long  ticks(long op);
  35. char *ticks2str(long ticks, short frmt);
  36. char *dosfind(char *file);
  37.  
  38.  
  39. static char *sort;                 /* Current sort order, for compare()      */
  40.  
  41. typedef struct {                   /* Data array (one per executable line)   */
  42.    char *file;                     /* ... Name of file to check size of      */
  43.    char *des;                      /* ... Descriptive message to display     */
  44.    char *name;                     /* ... Name of program to execute         */
  45.    char *cmdln;                    /* ... Command line to pass to program    */
  46.    char *err;                      /* ... Error message if any occur         */
  47.    long  tm;                       /* ... Clock ticks elasped on execution   */
  48.    long  size;                     /* ... Size in bytes of 'file'            */
  49.    short display;                  /* ... TRUE, if data should be displayed  */
  50.    }  EXLINE;
  51.  
  52.  
  53.  
  54. /*
  55. **  NAME
  56. **      DCCMP -- Dean Cooper's comparer program, Version 1.02
  57. **
  58. **      (C) Copyright 1989,1991 by Dean W. Cooper; All rights reserved
  59. **
  60. **      The DCCMP program and its source code is FREE for both private and
  61. **      commercial uses, and may be freely distributed as long as it is NOT
  62. **      sold for profit.  Please direct all correspondence to:
  63. **
  64. **                         Dean W. Cooper
  65. **                         3078 N Palo Verde
  66. **                         Tucson, AZ  85716
  67. **                         (602) 326-2403 (voice)
  68. **                         (516) 536-8723 Sound-of-Music BBS  
  69. **
  70. **
  71. **  DESCRIPTION
  72. **      DCCMP was written as a utility to do a comparison of archiver
  73. **      programs.  However, it is really a fairly general comparison program.
  74. **      It works in a manner similar to batch interpreters in that is simply
  75. **      reads and executes lines from a text file one at a time.  The usage
  76. **      for the program is as follows:
  77. **
  78. **         DCCMP [-#] [-q] [-tsn] [-o<file>] <batch_file> [<arg1> <arg2> ...]
  79. **
  80. **         -#            Number of times to execute batch file (1<=#<=9)
  81. **         -q            Quiet mode.  Forces 'echo off' for all of batch file.
  82. **         -tsn          Sort flags.  Each flag generates a separate output:
  83. **                           t  ... Sorted by time of execution
  84. **                           s  ... Sorted by size of resulting file
  85. **                           n  ... No sort
  86. **         -k<number>    Amount of memory to run programs in (x 1024)
  87. **         -z<arg0>      Set value of 'arg0', normally arg0=<current directory>
  88. **         -o<file>      Name of file to write  output data to
  89. **         -a<file>      Name of file to append output data to
  90. **         <batch_file>  Name of batch file to execute (assumes ".CMP")
  91. **         <argN>        Arguments to pass on to batch interpreter
  92. **                         (If no args are given, then the batch file's
  93. **                          usage information will be displayed.)
  94. **
  95. **      DCCMP reads the specified batch file which should be in the current
  96. **      directory or in the directory specified by the environment variable
  97. **      "DCCMP" (current directory searched first).  A batch file has the
  98. **      following format:
  99. **
  100. **         [<filename>] [;] [<description> ;]  <program>  [command line args]
  101. **
  102. **         -- Blank lines and lines preceded with '#' will be ignored.
  103. **
  104. **         -- Lines preceded with ':' will be displayed as the batch file's
  105. **            usage information.
  106. **
  107. **         -- Lines preceded with '>' will be displayed as a header to the
  108. **            output data.
  109. **
  110. **         -- DCCMP substitutes occurrences of '%1' with <arg1>, '%2' with
  111. **            <arg2>, etc.
  112. **
  113. **         -- Occurrences of '%0' are replaced with the current directory.
  114. **
  115. **         -- If the line has no ';', then the program is simply executed.
  116. **
  117. **         -- If the line has a ';', then the speed of the program's execution
  118. **            is timed and later displayed.
  119. **
  120. **         -- If the line has <filename> defined, then the size of the
  121. **            specified file is recorded (after the program is executed) and
  122. **            later displayed.
  123. **
  124. **         -- If the line has <description> defined, then that string will
  125. **            displayed when the statistical data is output (else the
  126. **            command line string will be used instead).
  127. **
  128. **         -- If a line is of the form:  '! label1  label2'  then the output
  129. **            will be split into two parts (those lines with <filename> defined,
  130. **            and the those without), and labeled with the the specified labels.
  131. **
  132. **         -- <program> can be one of the following:
  133. **
  134. **               DEL  <file1>..<fileN> Deletes files without the annoying
  135. **                                     "Are you sure?" prompt.
  136. **
  137. **               PUSH <full_path>      Save the current directory, and make the
  138. **                                     specified drive and directory current.
  139. **
  140. **               POP                   Restore the last current directory.
  141. **
  142. **               ECHO off              Turn off echoing of programs.
  143. **               ECHO on               Turn on  echoing of programs.
  144. **               ECHO <text>           Echo specified text to screen.
  145. **
  146. **               SUBNXT "str1" "str2"  Substitute on the next line only,
  147. **                                     occurances of 'str1' with 'str2'.
  148. **
  149. **  HISTORY
  150. **       2/21/89 DWC - Created
  151. */
  152.  
  153. main(argc, argv)
  154. int   argc;
  155. char *argv[];
  156. {
  157.    static EXLINE  d[ DSIZE ];      /* Array of executable line data          */
  158.    static char in[ INSZ ];         /* Buffer to read a batch file line in to */
  159.    static char sub[ INSZ ];        /* Buffer to do substitutions into        */
  160.    static char tmp[ INSZ ];        /* Temporary buffer for strings           */
  161.    static char dfile[ PATHSZ ];    /* Name of batch file to execute          */
  162.    static char dir[5][ PATHSZ ];   /* Stack of current directories           */
  163.    static char pat[ PATSZ ];       /* Buffer to hold substitution pattern    */
  164.    static char rep[ PATSZ ];       /* Buffer to hold pattern's replacement   */
  165.    static char *sorts[10];         /* Pointer to each sort to be generated   */
  166.    static char label1[ LABSZ ];    /* Label one from a '!' line              */
  167.    static char label2[ LABSZ ];    /* Label two from a '!' line              */
  168.  
  169.    short  numsorts=0;              /* Number of sorts defined                */
  170.    FILE  *f;                       /* File stream for batch file             */
  171.    FILE  *fo=stdout;               /* File stream for output data            */
  172.    short  tms=1;                   /* Number of times to execute batch file  */
  173.    short  frst=2;                  /* Argument substituted for '%1'          */
  174.    short  lvl=0;                   /* Stack level                            */
  175.    short  dsplusage=0;             /* TRUE, if displaying batch file usage   */
  176.    short  quiet=0;                 /* TRUE, if not echoing all lines         */
  177.    short  noecho=0;                /* TRUE, if not in echo mode              */
  178.    short  split=1;                 /* 2, if output being split into two parts*/
  179.    short  sortsz;                  /* TRUE, if sorting output by size        */
  180.    short  numt=0;                  /* Number of 't' sort flags encountered   */
  181.    short  nums=0;                  /* Number of 's' sort flags encountered   */
  182.    short  numn=0;                  /* Number of 'n' sort flags encountered   */
  183.    short  kmem=0;                  /* K of memory to run programs in         */
  184.    short  actual=0;                /* Actual amount of memory for programs   */
  185.    char huge *mem;                 /* Pointer to memory eaten                */
  186.    long   tm_one;                  /* Time to run one program                */
  187.    long   tm_run;                  /* Time into current run of batch         */
  188.    long   tm_entire;               /* Time of all runs                       */
  189.    long   tm_perrun;               /* Time to go through batch file one time */
  190.    char  *white = " \t";           /* All characters considered white space  */
  191.    char  *empty = "";              /* The empty string                       */
  192.    char  *arg0 = NULL;             /* Pointer to value of 'arg0'             */
  193.  
  194.    char  *s, *s1, *s2, *s3, *s4, c;
  195.    short  i, j, k, m, num, p, q, rtn;
  196.    float  rel;
  197.    long   base;
  198.    struct stat buff;
  199.  
  200.  
  201.  
  202.    /*
  203.    ** Parse any options on command line.
  204.    */
  205.  
  206.    for (i=1 ; i<argc ; i++)
  207.       if (argv[i][0] != '-')
  208.          break;
  209.       else
  210.          for (j=1 ; c = argv[i][j] ; j++)
  211.             switch (c = tolower(c)) {
  212.                case 'q':  quiet=1;   break;
  213.  
  214.                case 't':
  215.                   if (numt++)  break;
  216.                   sorts[ numsorts++ ] = "ts";
  217.                   break;
  218.  
  219.                case 's':
  220.                   if (nums++)  break;
  221.                   sorts[ numsorts++ ] = "st";
  222.                   break;
  223.  
  224.                case 'n':
  225.                   if (numn++)  break;
  226.                   sorts[ numsorts++ ] = "n";
  227.                   break;
  228.  
  229.                case 'k':
  230.                   kmem = atoi( &argv[i][j+1] );
  231.  
  232.                   for ( ; c = argv[i][j+1] ; j++)
  233.                      if (c<'0' || c>'9')
  234.                         break;
  235.                   break;
  236.  
  237.                case 'z':
  238.                   arg0 = &argv[i][j+1];
  239.  
  240.                   j = strlen(argv[i]) - 1;
  241.                   break;
  242.  
  243.                case 'o':
  244.                   if (!(fo = fopen(s = &argv[i][j+1], "w")))
  245.                      printf("ERROR: Could not open output file: %s\n", s),  exit(1);
  246.  
  247.                   j = strlen(argv[i]) - 1;
  248.                   break;
  249.  
  250.                case 'a':
  251.                   if (!(fo = fopen(s = &argv[i][j+1], "a")))
  252.                      printf("ERROR: Could not open output file: %s\n", s),  exit(1);
  253.  
  254.                   j = strlen(argv[i]) - 1;
  255.                   break;
  256.  
  257.                default:
  258.                   if ((k=c-'0') >=1 && k<=9)   tms = k;
  259.                   else                         usage();
  260.                }
  261.  
  262.    frst = i+1;
  263.  
  264.    if (i>=argc)  usage();
  265.  
  266.  
  267.  
  268.    /*
  269.    ** If batch file name has no extension, put ".CMP" on.
  270.    */
  271.  
  272.    s = strupr( strcpy(dfile,argv[frst-1]) );
  273.  
  274.    if (!strrchr(s,'.'))
  275.       strcat(s, ".CMP");
  276.  
  277.  
  278.  
  279.    /*
  280.    ** Open batch file.
  281.    */
  282.  
  283.    if (!(f = fopen(s, "r")))
  284.       if (s = getenv("DCCMP")) {
  285.          strcpy(in,dfile);
  286.          strcpy(dfile,s);
  287.          if (s[strlen(s)-1]!='\\') strcat(dfile, "\\");
  288.          strcat(dfile,in);
  289.  
  290.          f = fopen(dfile, "r");
  291.          }
  292.  
  293.    if (!f)
  294.       printf("ERROR: Cannot open batch file: %s\n", dfile),  exit(1);
  295.  
  296.  
  297.    /*
  298.    ** Save current directory.
  299.    */
  300.  
  301.    getpath( dir[lvl] );
  302.  
  303.    if (!arg0)  arg0 = dir[lvl];
  304.  
  305.  
  306.    /*
  307.    ** Set flag if we're to display the batch file's usage.
  308.    */
  309.  
  310.    dsplusage = argc==frst;
  311.  
  312.  
  313.  
  314.    /*
  315.    ** Read in one line at a time from data file.
  316.    */
  317.  
  318.    for (j=0 ; j<DSIZE && fgets(in, INSZ, f) ; ) {
  319.  
  320.          /*
  321.          ** Remove trailing newline (if any).
  322.          */
  323.  
  324.          if (in[i=strlen(in)-1]=='\n')  in[i]=0;
  325.  
  326.  
  327.          /*
  328.          ** If just display batch file's usage, then do it.
  329.          */
  330.  
  331.          if (dsplusage)
  332.             if (in[0]==':')  puts(in+1);
  333.             else             continue;
  334.  
  335.  
  336.          /*
  337.          ** Skip comment lines and blank lines.
  338.          */
  339.  
  340.          if (in[0]=='#' || in[0]==':' || !in[0])
  341.             continue;
  342.  
  343.  
  344.          /*
  345.          ** See if this is a command line to split output into two parts.
  346.          */
  347.  
  348.          if (in[0]=='!') {
  349.             split = 2;
  350.  
  351.             if (s = strtok(in+1, white)) {
  352.                strcpy(label1, s);
  353.  
  354.                if (s = strtok(NULL, white))
  355.                   strcpy(label2, s);
  356.                }
  357.  
  358.             continue;
  359.             }
  360.  
  361.  
  362.          /*
  363.          ** Clear the entry for this line.
  364.          */
  365.  
  366.          d[j].file    = d[j].name    = d[j].des    = d[j].cmdln    = empty;
  367.          d[j].file[0] = d[j].name[0] = d[j].des[0] = d[j].cmdln[0] = d[j].size = d[j].tm = d[j].display = 0;
  368.  
  369.  
  370.          /*
  371.          ** Expand occurances of '%0', '%1', etc, into arguments from command line.
  372.          */
  373.  
  374.          for (s=strcpy(sub,in) ; s = strchr(s, '%') ; strcpy(in,sub))
  375.             if (!( (i = s[1]-'0') >=0  && i<=9 ))
  376.                s++;
  377.             else
  378.                if      (i == 0)       strcpy(s, arg0),            strcat(s, in+(s-sub)+2);
  379.                else if (i>argc-frst)  strcpy(s, in+(s-sub)+2);
  380.                else                   strcpy(s, argv[i+frst-1]),  strcat(s, in+(s-sub)+2);
  381.  
  382.  
  383.          /*
  384.          ** See if line is a output header comment line.
  385.          */
  386.  
  387.          if (in[0]=='>') {
  388.             d[j].cmdln = strdup(in+1);  d[j++].size= COMMENT;  continue; }
  389.  
  390.  
  391.          /*
  392.          ** Get copy of file name part of line.
  393.          */
  394.  
  395.          if (!(s = strchr(in, ';')))   s=in-1;
  396.          else {
  397.             d[j].display = 1;
  398.  
  399.             if (*(in + strspn(in," \t")) != ';')
  400.                d[j].file = strdup( strtok(strcpy(sub,in), " \t;") );
  401.             }
  402.  
  403.          strcpy(sub, s+1);
  404.          strcpy(in,  sub);
  405.  
  406.  
  407.          /*
  408.          ** Get copy of description part of line.
  409.          */
  410.  
  411.          if (!(s = strchr(in, ';')))   s=in-1;
  412.          else {
  413.             if (*(s1 = in + strspn(in," \t")) != ';')
  414.                strncpy(tmp, s1, s-s1),  tmp[ s-s1 ]=0,  d[j].des=strdup(tmp);
  415.             }
  416.  
  417.          strcpy(sub, s+1);
  418.  
  419.  
  420.  
  421.          /*
  422.          ** Get copy of program name part and command line part of line.
  423.          */
  424.  
  425.          if (s = strtok(strcpy(in,sub), white))  d[j].name = strdup(s);
  426.          else                                    continue;
  427.  
  428.          strupr(d[j].name);
  429.  
  430.          if (s = strtok(NULL, white)) {   /* We now know there is a cmd line */
  431.             s = sub + strspn(sub,white);  /* ... Skip white space before name*/
  432.             s =       strpbrk(s, white);  /* ... Skip over program name      */
  433.  
  434.             if (strcmp(d[j].name, "ECHO"))
  435.                s = s + strspn(s,  white); /* ... Skip white space after name */
  436.  
  437.             d[j].cmdln = strdup(s);
  438.             }
  439.  
  440.  
  441.          j++;
  442.          }
  443.  
  444.  
  445.    fclose(f);
  446.  
  447.    if (dsplusage)
  448.       exit(0);
  449.  
  450.  
  451.  
  452.    /*
  453.    ** See if there was any allocation errors.
  454.    */
  455.  
  456.    for (num=j, j=0 ; j<num ; j++)
  457.       if (!d[j].file || !d[j].name || !d[j].cmdln || !d[j].des)
  458.          printf("ERROR: Out of memory.\n"),  exit(1);
  459.  
  460.  
  461.    /*
  462.    ** Reduce amount of memory that programs have to run in.
  463.    */
  464.  
  465.    if (kmem) {
  466.       for (i=600 ; i>1 && (mem = halloc((long) i,1024)) == NULL ; i--);
  467.  
  468.       hfree(mem);
  469.  
  470.       if (i>kmem)
  471.          if (!(mem = halloc((long) (i-kmem), 1024)))
  472.             printf("ERROR: Huge memory allocation error.\n"), exit(1);
  473.       }
  474.  
  475.    for (i=600 ; i>1 && (mem = halloc((long) i,1024)) == NULL ; i--);
  476.    actual = i;
  477.    hfree(mem);
  478.  
  479.  
  480.  
  481.    /*
  482.    ** Start clock on entire run of batch.
  483.    */
  484.  
  485.    tm_entire = tm_run = ticks(START);
  486.  
  487.  
  488.  
  489.    /*
  490.    ** Run through again and execute programs.
  491.    */
  492.  
  493.    for (i=0 ; i<tms ; i++) {
  494.       if (i)  tm_run = ticks(START);
  495.  
  496.       for (m=j=0 ; j<num ; j++)
  497.          if (d[j].size>=0) {
  498.  
  499.             s = d[j].name;
  500.             strcpy(in, d[j].cmdln);
  501.  
  502.  
  503.             /*
  504.             ** Execute echo command before any commands are 'echoed'.
  505.             */
  506.  
  507.             if (!strcmp(s, "ECHO")) {
  508.                s     = strupr(strtok(in,white));
  509.  
  510.                if      (!strcmp(s, "OFF"))  noecho = 1;
  511.                else if (!strcmp(s, "ON"))   noecho = 0;
  512.                else
  513.                   printf("%s\n", d[j].cmdln);
  514.  
  515.                continue;
  516.                }
  517.  
  518.  
  519.             /*
  520.             ** If not in quite mode, display program we're executing.
  521.             */
  522.  
  523.             if (!quiet && !noecho) {
  524.                printf("\nRun: %d   Line: %2d   Time elapsed: %7s   Time to go: ", i+1, ++m, ticks2str(ticks(tm_entire), HOURS));
  525.  
  526.                if (!i) printf("Unknown\n");
  527.                else    printf("%7s\n", ticks2str(tm_perrun * (tms-i-1) + tm_perrun - ticks(tm_run), HOURS) );
  528.  
  529.                printf("[ %s ] %s %s\n", dir[lvl], norm(d[j].name), d[j].cmdln);
  530.                }
  531.  
  532.  
  533.             /*
  534.             ** Execute internally defined commands.
  535.             */
  536.  
  537.             if (!strcmp(s, "PUSH")) {
  538.                setpath( strtok(in, white) );
  539.                getpath( dir[++lvl] );
  540.  
  541.                continue;
  542.                }
  543.             else if (!strcmp(s, "POP")) {
  544.                if (lvl>0)
  545.                   setpath( dir[--lvl] ),   getpath( dir[lvl] );
  546.  
  547.                continue;
  548.                }
  549.             else if (!strcmp(s, "DEL")) {
  550.                s = strtok(in, white);
  551.  
  552.                do {
  553.                   if (s = dosfind(s))
  554.                      while (s)
  555.                         remove(s),  s=dosfind(NULL);
  556.  
  557.                   } while (s = strtok(NULL, white));
  558.  
  559.                continue;
  560.                }
  561.  
  562.             /*
  563.             ** Do pattern substition on following line's command line.
  564.             */
  565.  
  566.             else if (!strcmp(s, "SUBNXT")) {
  567.                s  = d[j].cmdln;
  568.                if ((s1 = strchr(s, '"'))    && (s2 = strchr(s1+1, '"')) &&
  569.                    (s3 = strchr(s2+1, '"')) && (s4 = strchr(s3+1, '"')) && j+1<num) {
  570.  
  571.                   /*
  572.                   ** Get pattern to search for and it's replacement.
  573.                   */
  574.  
  575.                   strncpy(pat, s1+1, (s2-s1)-1),   pat[ (s2-s1)-1 ] = 0;
  576.                   strncpy(rep, s3+1, (s4-s3)-1),   rep[ (s4-s3)-1 ] = 0;
  577.  
  578.                   s = d[j+1].cmdln;
  579.                   k = strlen(pat);
  580.  
  581.                   /*
  582.                   ** Now do the substition.
  583.                   */
  584.  
  585.                   if (pat[0])
  586.                      for (s1=strcpy(sub,s) ; s1 = strchr(s1, pat[0]) ; strcpy(s,sub))
  587.                         if (!strncmp(s1, pat, k))
  588.                            strcpy(s1, rep),  strcat(s1, s+(s1-sub)+k);
  589.                         else
  590.                            s1++;
  591.                   }
  592.  
  593.                continue;
  594.                }
  595.  
  596.  
  597.  
  598.             /*
  599.             ** Execute and time regular DOS program.
  600.             */
  601.  
  602.             tm_one = ticks(START);
  603.  
  604.             if (rtn = spawnlp(P_WAIT, d[j].name, d[j].name, d[j].cmdln, NULL)) {
  605.                if (rtn != -1)
  606.                   printf("ERROR: Program returned error value of: %d\n", rtn);
  607.                else if (errno == E2BIG)   printf("ERROR: Command line <%s> too long.\n", d[j].cmdln);
  608.                else if (errno == ENOENT)  printf("ERROR: Program not found: <%s>\n", norm(d[j].name));
  609.                else if (errno == ENOEXEC) printf("ERROR: Invalid executable format: <%s>\n", norm(d[j].name));
  610.                else if (errno == ENOMEM)  printf("ERROR: Not enough memory to run: <%s>\n", norm(d[j].name));
  611.  
  612.                if (rtn != -1)
  613.                   sprintf(tmp, "ERROR: Exit value of: %d", rtn),  d[j].err=strdup(tmp);
  614.                else if (errno == E2BIG)   d[j].err = "ERROR: Command line too long";
  615.                else if (errno == ENOENT)  d[j].err = "ERROR: Program not found";
  616.                else if (errno == ENOEXEC) d[j].err = "ERROR: Invalid EXE file";
  617.                else if (errno == ENOMEM)  d[j].err = "ERROR: Not enough memory";
  618.  
  619.                d[j].size = ERROR;
  620.                continue;
  621.                }
  622.  
  623.             d[j].tm += ticks(tm_one);
  624.  
  625.  
  626.             /*
  627.             ** Record resulting file size if it was specified.
  628.             */
  629.  
  630.             if (d[j].file[0]) {
  631.                if (stat(d[j].file,&buff)) {
  632.                   printf("ERROR: File not found: <%s>\n", d[j].file);
  633.                   d[j].err  = "ERROR: File was not found";
  634.                   d[j].size = ERROR;
  635.                   continue;
  636.                   }
  637.  
  638.                d[j].size += buff.st_size;
  639.                }
  640.             }
  641.  
  642.       tm_perrun = ticks(tm_entire)/(i+1);
  643.       }
  644.  
  645.  
  646.  
  647.    /*
  648.    ** Calculate total time elasped.
  649.    */
  650.  
  651.    tm_entire = ticks(tm_entire);
  652.    tm_perrun = tm_entire/tms;
  653.  
  654.  
  655.  
  656.    /*
  657.    ** Display header for output of data.
  658.    */
  659.  
  660.    fprintf(fo, "\n\n\n------------------------------------------------------------------------------\n");
  661.    fprintf(fo, "This output was generated by:\n");
  662.    fprintf(fo, "DCCMP - Dean Cooper's comparer program, Version %s\n\n", VERSION);
  663.  
  664.    fprintf(fo, "DCCMP was run as: \"DCCMP");
  665.  
  666.    for (i=1 ; i<argc ; i++)
  667.       fprintf(fo, " %s", argv[i]);
  668.  
  669.    fprintf(fo,"\"\n\n  Batch %8s was run: %7d time%s...\n", strupr(argv[frst-1]), tms, tms>1 ? "s" : "");
  670.    fprintf(fo, "Memory free for programs: %7d K\n", actual);
  671.    fprintf(fo, "            Time per run: %7s\n", ticks2str(tm_perrun, HOURS));
  672.    fprintf(fo, "      Total time elapsed: %7s\n", ticks2str(tm_entire, HOURS));
  673.  
  674.  
  675.    /*
  676.    ** Display header comment lines (if any).
  677.    */
  678.  
  679.    for (j=0 ; j<num ; j++)
  680.       if (d[j].size == COMMENT)
  681.          fprintf(fo, "%s\n", d[j].cmdln);
  682.  
  683.  
  684.    /*
  685.    ** Run through again and display results.  If split==2, then we are
  686.    ** separating the output of those lines with file sizes, from those
  687.    ** without.  Also, we display once for each requested sort.
  688.    */
  689.  
  690.    for (k=0 ; k<split ; k++)
  691.       for (i=0 ; i<numsorts || i<1 ; i++) {
  692.  
  693.          s = k==0 ? label1 : label2;
  694.  
  695.  
  696.          /*
  697.          ** Sort data if any sort is asked for.
  698.          */
  699.  
  700.          if (numsorts) {
  701.             sort = strlwr( sorts[i] );
  702.             qsort(&d[0], num, sizeof(d[0]), compare);
  703.             }
  704.  
  705.  
  706.          /*
  707.          ** Display data if sorting by size or speed.
  708.          */
  709.  
  710.          if (numsorts && sort[0]!='n') {
  711.  
  712.             sortsz = sort[0]=='s';
  713.  
  714.             if (k==1 && sortsz && numsorts>1)
  715.                continue;
  716.  
  717.             if (s[0])  fprintf(fo, "\n\n%s, s", s);
  718.             else       fprintf(fo, "\n\nS");
  719.  
  720.             fprintf(fo, "orted by: %s", sortsz && (split<2 || k==0) ? "Size" : "Speed");
  721.  
  722.  
  723.             fprintf(fo, "\n\nProgram   Description             Ticks   Min:Secs  %s%s\n", (split<2 || k==0) ? "Size      " : "", (split>1 && sortsz && k==1) ? "" : "Relative");
  724.             fprintf(fo,     "========  ======================  ======  ========  %s%s\n", (split<2 || k==0) ? "========  " : "", (split>1 && sortsz && k==1) ? "" : "========");
  725.  
  726.             for (m=j=0 ; j<num ; j++)
  727.                if (d[j].size>=ERROR  &&  d[j].display) {
  728.                   s = d[j].des[0] ? d[j].des : d[j].cmdln;
  729.  
  730.                   p = d[j].size >0 && (split<2 || k==0);
  731.                   q = d[j].size<=0 && (split<2 || k==1);
  732.  
  733.                   if (d[j].size==ERROR && (p || q)) {
  734.                      fprintf(fo, "%-8s  %-22s  %s\n", norm(d[j].name), s, d[j].err);  continue;  }
  735.  
  736.                   if (sortsz && q)
  737.                      fprintf(fo, "%-8s  %-22s  %6ld  %8s\n", norm(d[j].name), s, d[j].tm/tms, ticks2str(d[j].tm/tms, MINS));
  738.                   else {
  739.                      if (p || q) {
  740.                         if (!m++)
  741.                            base = sortsz ? d[j].size : d[j].tm;
  742.  
  743.                         if (sortsz)  rel = ((float) d[j].size)/base;
  744.                         else         rel = ((float) d[j].tm  )/base;
  745.                         }
  746.  
  747.                      if (p)  fprintf(fo, "%-8s  %-22s  %6ld  %8s  %8ld  %8.2f\n", norm(d[j].name), s, d[j].tm/tms, ticks2str(d[j].tm/tms, MINS), d[j].size/tms,               rel);
  748.                      if (q)  fprintf(fo, "%-8s  %-22s  %6ld  %8s  %s%8.2f\n",     norm(d[j].name), s, d[j].tm/tms, ticks2str(d[j].tm/tms, MINS), split<2 ? "          " : "", rel);
  749.                      }
  750.                   }
  751.             }
  752.          else {
  753.  
  754.             /*
  755.             ** Display data if Not sorting.
  756.             */
  757.  
  758.             if (s[0])           fprintf(fo, "\n\n%s%s:", s, numsorts ? ", not sorted" : "");
  759.             else if (numsorts)  fprintf(fo, "\n\nNot sorted:");
  760.  
  761.  
  762.             fprintf(fo, "\n\nProgram   Description             Ticks   Min:Secs%s\n", (split<2 || k==0) ? "  Size      " : "");
  763.             fprintf(fo,     "========  ======================  ======  ========%s\n", (split<2 || k==0) ? "  ========  " : "");
  764.  
  765.             for (j=0 ; j<num ; j++)
  766.                if (d[j].size>=ERROR  &&  d[j].display) {
  767.                   s = d[j].des[0] ? d[j].des : d[j].cmdln;
  768.  
  769.                   p = d[j].size >0 && (split<2 || k==0);
  770.                   q = d[j].size<=0 && (split<2 || k==1);
  771.  
  772.                   if (d[j].size==ERROR && (p || q)) {
  773.                      fprintf(fo, "%-8s  %-22s  %s\n", norm(d[j].name), s, d[j].err);  continue;  }
  774.  
  775.                   if (p)  fprintf(fo, "%-8s  %-22s  %6ld  %8s  %8ld\n", norm(d[j].name), s, d[j].tm/tms, ticks2str(d[j].tm/tms, MINS), d[j].size/tms);
  776.                   if (q)  fprintf(fo, "%-8s  %-22s  %6ld  %8s\n",       norm(d[j].name), s, d[j].tm/tms, ticks2str(d[j].tm/tms, MINS));
  777.                   }
  778.             }
  779.          }
  780.  
  781.    exit(0);
  782. }
  783.  
  784.  
  785. usage()
  786. {
  787.    printf("DCCMP - Dean Cooper's comparer program, Version %s\n", VERSION);
  788.    printf("(C) Copyright 1989,1991 by Dean W. Cooper; All right reserved\n\n");
  789.  
  790.    printf("The DCCMP program and its source code is FREE for both private and commercial\n");
  791.    printf("uses, and may be freely distributed as long as it is NOT sold for profit.\n");
  792.    printf("Please direct all correspondence to:\n\n");
  793.  
  794.    printf("                      Dean W. Cooper\n");
  795.    printf("                      3078 N Palo Verde\n");
  796.    printf("                      Tucson, AZ  85716\n");
  797.    printf("                      (602) 326-2403 (voice)\n");
  798.    printf("                      (516) 536-8723 Sound-of-Music BBS\n");
  799.  
  800.    printf("\n  <hit any key>");
  801.    getch();
  802.  
  803.  
  804.    printf("\n\n\nDCCMP lets one compare programs by the speed of their execution, and\n");
  805.    printf("by the size of the file they produce.  DCCMP works in a manner similar\n");
  806.    printf("to batch interpreters in that it simply reads and executes lines from\n");
  807.    printf("a text file one at a time.  The usage is:\n\n");
  808.  
  809.    printf("   DCCMP  [-#] [-q] [-tsn] [-o<file>]  <batch_file>  [<arg1> <arg2>...]\n\n");
  810.  
  811.    printf("   -#             Number of times to execute batch file (1<=#<=9)\n");
  812.    printf("   -q             Quiet mode.  Forces 'echo off' for all of batch file.\n");
  813.    printf("   -tsn           Sort flags.  Each flag generates a separate output:\n");
  814.    printf("                      t  ... Sorted by Time of execution\n");
  815.    printf("                      s  ... Sorted by Size of resulting file\n");
  816.    printf("                      n  ... No sort\n");
  817.    printf("   -k<number>     Amount of memory to run programs in (x 1024)\n");
  818.    printf("   -z<arg0>       Set value of 'arg0', normally arg0=<current directory>\n");
  819.    printf("   -o<file>       Name of file to write  output data to\n");
  820.    printf("   -a<file>       Name of file to append output data to\n");
  821.    printf("   <batch_file>   Name of batch file to execute (assumes \".CMP\")\n");
  822.    printf("   <argN>         Arguments to pass on to batch interpreter\n");
  823.    printf("                    (If no args are given, then the batch file's\n");
  824.    printf("                     usage information will be displayed.)\n");
  825.  
  826.    printf("\n  <hit any key>");
  827.    getch();
  828.  
  829.  
  830.    printf("\n\n\n\n\nDCCMP reads the specified batch file which should be in the current\n");
  831.    printf("directory or in the directory specified by the environment variable\n");
  832.    printf("\"DCCMP\" (current directory searched first).\n\n");
  833.  
  834.    printf("   A batch file has the following format:\n\n");
  835.  
  836.    printf("   [filename] [;] [<description> ;]  <program>  [command line args]\n\n");
  837.  
  838.    printf("   -- Blank lines and lines preceded with '#' will be ignored.\n");
  839.    printf("   -- Lines preceded with ':' will be displayed as the batch file's\n");
  840.    printf("        usage information.\n");
  841.    printf("   -- Lines preceded with '>' will be displayed as a header to the\n");
  842.    printf("        output data.\n");
  843.    printf("   -- DCCMP substitutes occurrences of '%%1' with <arg1>, '%%2' with\n");
  844.    printf("        <arg2>, etc.\n");
  845.    printf("   -- Occurrences of '%%0' are replaced with the current directory.\n");
  846.    printf("   -- If the line has no ';', then the program is simply executed.\n");
  847.    printf("   -- If the line has a ';', then the speed of the program's execution\n");
  848.    printf("        is timed and later displayed.\n");
  849.    printf("   -- If the line has <filename> defined, then the size of the\n");
  850.    printf("        specified file is recorded (after the program is executed) and\n");
  851.    printf("        later displayed.\n");
  852.  
  853.    printf("\n  <hit any key>");
  854.    getch();
  855.  
  856.    printf("\n\n\n\n\n\n   -- If the line has <description> defined, then that string will\n");
  857.    printf("        displayed when the statistical data is output (else the\n");
  858.    printf("        command line string will be used instead).\n");
  859.    printf("   -- If a line is of the form:  '! label1  label2'  then the output\n");
  860.    printf("        will be split into two parts (those lines with <filename> defined,\n");
  861.    printf("        and the those without), and labeled with the the specified labels.\n\n");
  862.  
  863.    printf("   The following are internally defined programs:\n\n");
  864.  
  865.    printf("   DEL  <file1>..<fileN>  Deletes files without the annoying\n");
  866.    printf("                            \"Are you sure?\" prompt.\n");
  867.    printf("   PUSH <full_path>       Save the current directory, and make the\n");
  868.    printf("                            specified drive and directory current.\n");
  869.    printf("   ECHO off               Turn off echoing of programs.\n");
  870.    printf("   ECHO on                Turn on  echoing of programs.\n");
  871.    printf("   ECHO <text>            Echo specified text to screen.\n");
  872.    printf("   POP                    Restore the last current directory.\n");
  873.    printf("   SUBNXT \"str1\" \"str2\"   Substitute on the next line only,\n");
  874.    printf("                            occurances of 'str1' with 'str2'.\n");
  875.  
  876.    exit(2);
  877. }
  878.  
  879.  
  880.  
  881. /*
  882. **  NAME
  883. **      norm -- Return pointer to normal part of filename
  884. */
  885.  
  886. char *norm(char *file)
  887. {
  888.    int i;
  889.  
  890.    for (i=strlen(file)-1 ; i>=0 ; i--)
  891.       if (file[i]=='\\' || file[i]==':')
  892.          break;
  893.  
  894.    return &file[i+1];
  895. }
  896.  
  897.  
  898.  
  899. /*
  900. **  NAME
  901. **      compare -- This function is called by the 'qsort' routine
  902. */
  903.  
  904. int compare(e1, e2)
  905. EXLINE *e1, *e2;
  906. {
  907.    short i, j=0;
  908.    char  c;
  909.    long  l;
  910.  
  911.    for (i=0 ; (c=tolower(sort[i])) && !j ; i++)
  912.       if      (c == 't')  j = (int) (l = e1->tm   - e2->tm,   (l<0 ? -1 : (l>0 ? 1 : 0)));
  913.       else if (c == 's')  j = (int) (l = e1->size - e2->size, (l<0 ? -1 : (l>0 ? 1 : 0)));
  914.       else if (c == 'n')  j = e1>e2 ? 1 : (e1<e2 ? -1 : 0);
  915.  
  916.    return j;
  917. }
  918.  
  919.  
  920.  
  921.  
  922. /*
  923. **  NAME
  924. **      getpath -- Get current drive and path
  925. **
  926. **  SYNOPSIS
  927. **      void getpath( s );
  928. **
  929. **      char *s;    --  Pointer to where to put current drive and path
  930. **
  931. **  DESCRIPTION
  932. **      This function simply returns DOS's currently logged drive and path.
  933. **      The return string will be terminated with a slash: '\'.
  934. */
  935.  
  936. void getpath(cur)
  937. char *cur;
  938. {
  939.    getcwd(cur, 80);
  940.    strupr(cur);
  941.    if (cur[strlen(cur)-1] != '\\')  strcat(cur, "\\");
  942. }
  943.  
  944.  
  945.  
  946. /*
  947. **  NAME
  948. **      setpath -- Set current drive and path
  949. **
  950. **  SYNOPSIS
  951. **      void setpath( s );
  952. **
  953. **      char *s;    --  Pointer drive and directory to set
  954. **
  955. **  DESCRIPTION
  956. **      This function simply sets current the specified drive and directory.
  957. */
  958.  
  959. void setpath(s)
  960. char *s;
  961. {
  962.    short k;
  963.  
  964.    k=strlen(s) - 1;
  965.  
  966.    if      (k<0 || s[k]==':')  strcat(s, "\\");
  967.    else if (s[k]=='\\' && k>0) s[k]=0;
  968.  
  969.    if (s[1]==':')
  970.       bdos(0x0E, toupper(s[0]) - 'A', 0);
  971.  
  972.    chdir(s);
  973. }
  974.  
  975.  
  976.  
  977. /*
  978. **  NAME
  979. **      ticks -- Return current time or number of ticks that have elapsed
  980. **
  981. **  SYNOPSIS
  982. **      long ticks( op );
  983. **
  984. **      long op;   --  START,     to return current time
  985. **                     last_time, to return elapsed ticks since 'last_time'
  986. **
  987. **  DESCRIPTION
  988. **      When this function is called with START, it returns the current time.
  989. **      Then when the function is called with a 'time', it returns the number
  990. **      of ticks that have elapsed since that 'time'.
  991. */
  992.  
  993. long ticks(op)
  994. long op;
  995. {
  996.    static unsigned long last=0L, overflow=0L;
  997.    unsigned long cur;
  998.    union    REGS regs;
  999.  
  1000.    regs.x.ax = 0;
  1001.    int86(0x1A, ®s, ®s);
  1002.  
  1003.    cur  = regs.x.dx;
  1004.    cur |= ((unsigned long) regs.x.cx) << 16;
  1005.  
  1006.    if (cur < last)  overflow+=1573039L;
  1007.  
  1008.    last  = cur;
  1009.    cur  += overflow;
  1010.  
  1011.    if (op!=START)  cur = cur - op;
  1012.  
  1013.    return (long) cur;
  1014. }
  1015.  
  1016.  
  1017.  
  1018. /*
  1019. **  NAME
  1020. **      ticks2str -- Convert number of ticks to string with hours/minutes/secs
  1021. **
  1022. **  SYNOPSIS
  1023. **      char *ticks2str( ticks, format );
  1024. **
  1025. **      long  ticks;    --  Number of ticks
  1026. **      short format;   --  HOURS, for string in "hh:mm:ss" format
  1027. **                          MINS,  for string in "mmm:ss.t" format
  1028. **
  1029. **  DESCRIPTION
  1030. **      This function converts the number of ticks to a string with hours,
  1031. **      minutes, and seconds (8 characters long).
  1032. */
  1033.  
  1034. char *ticks2str(ticks, frmt)
  1035. long  ticks;
  1036. short frmt;
  1037. {
  1038.    static char str[10];
  1039.    short  hours, min;
  1040.    float  secs;
  1041.  
  1042.  
  1043.    secs  = ticks / 18.2;
  1044.    min   = secs  / 60;
  1045.    hours = min   / 60;
  1046.  
  1047.  
  1048.    if (frmt == MINS) {
  1049.       secs = ticks / 18.2 - min * 60;
  1050.  
  1051.       sprintf(str, "%03d:%04.1f", min, secs);
  1052.       }
  1053.    else {
  1054.       secs  = secs - min   * 60;
  1055.       min   = min  - hours * 60;
  1056.  
  1057.       sprintf(str, "%d:%02d:%02d", hours, min, (int) secs);
  1058.       }
  1059.  
  1060.    return str;
  1061. }
  1062.  
  1063.  
  1064.  
  1065. /*
  1066. **  NAME
  1067. **      dosfind -- Find first and next DOS files with wildcard expansion
  1068. **
  1069. **  SYNOPSIS
  1070. **      char *dosfind( file );
  1071. **
  1072. **      char *file;   -- Name of file (with wildcards) to match
  1073. **                       NULL, to return next match
  1074. **
  1075. **  RETURNS
  1076. **      char *match;  -- Returns next file that matched
  1077. **
  1078. **  DESCRIPTION
  1079. **      This function does DOS wildcard expansion to find the first and
  1080. **      next files that match the specified file spec (only 'normal' files
  1081. **      are found).
  1082. */
  1083.  
  1084. #define DIR_ATTR        0x10
  1085. #define SEGMENT(p)      (*((unsigned short*)&(p)+1))
  1086. #define OFFSET(p)       (*((unsigned short*)&(p)))
  1087.  
  1088. char *dosfind(file)
  1089. char *file;
  1090. {
  1091.    static char  org[ PATHSZ ];
  1092.    static short i;
  1093.  
  1094.    static struct {
  1095.       char     res[21];
  1096.       char     attr;
  1097.       unsigned time;
  1098.       unsigned date;
  1099.       long     size;
  1100.       char     name[13];
  1101.       } dta, *p=&dta;
  1102.  
  1103.    union REGS  regs;
  1104.  
  1105.  
  1106.    bdos(0x1a, OFFSET(p), 0);            /* Set data transfer area (DTA)      */
  1107.  
  1108.    if (file) {
  1109.       strcpy(org,file);                 /* Save file's path                  */
  1110.  
  1111.       i = strlen(org);                  /* Find where path part of name ends */
  1112.       for (i-- ; i>=0 && org[i]!='\\' && org[i]!=':'  ; i--);
  1113.       i++;
  1114.  
  1115.       regs.h.ah = 0x4e;                 /* Search for first match            */
  1116.       regs.x.cx = 0x00;                 /* Get normal files only             */
  1117.       regs.x.dx = OFFSET(file);         /* Search string                     */
  1118.       }
  1119.    else
  1120.       regs.h.ah = 0x4f;                 /* Search for next match             */
  1121.  
  1122.  
  1123.    intdos(®s, ®s);
  1124.  
  1125.  
  1126.    if (regs.x.cflag)                    /* If NO match return NULL           */
  1127.       return NULL;
  1128.  
  1129.    strcpy(org+i, dta.name);             /* Else, tack name onto file path    */
  1130.  
  1131.    return  org;                         /* And return pointer to full name   */
  1132. }
  1133.